﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;

namespace Utility.BehaviourTree
{
	public enum Status
	{
		Success,
		Failure,
		Running,
		Inactive,
		Error,
	}

	public abstract class BTNode
	{
		public BTNode ParentNode { get; protected set; } = null;
		public Status Status { get; protected set; } = Status.Inactive;

		public string Information { get; protected set; } = null;

		string _showname;
		public string Name
		{
			get
			{
				if (string.IsNullOrEmpty (_showname))
				{
					_showname = GetShowName ();
				}
				return _showname;
			}
			set { _showname = value; }
		}
		public string FullName
		{
			get
			{
				if (ParentNode == null)
				{
					return Name;
				}
				else
				{
					return ParentNode.FullName + "." + Name;
				}
			}
		}

		protected DataBoard _databoard;
		public DataBoard DataBoard
		{
			get
			{
				if (_databoard == null)
				{
					if (ParentNode == null)
					{
						Debug.LogError (FullName + ": Parent is null, thus cannot find Databoard");
					}
					_databoard = ParentNode.DataBoard;
					if (_databoard == null)
					{
						Debug.LogError (FullName + ": Cannot find DataBoard");
					}
				}
				return _databoard;
			}
			protected set { _databoard = value; }
		}

		public bool IsInactive { get { return Status == Status.Inactive; } }
		public bool IsRunning { get { return Status == Status.Running; } }
		public bool IsDone { get { return Status == Status.Success || Status == Status.Failure; } }
		public bool IsError { get { return Status == Status.Error; } }

		public Status Update ()
		{
			if (IsDone)
			{
				Reset ();
			}

			if (!OnPreUpdate ())
			{
				return Status.Error;
			}

			if (!IsRunning)
			{
				if (!OnStart ())
				{
					Status = Status.Failure;
					return Status;
				}
			}
			Status = OnUpdate ();

			if (OnPostUpdate ())
			{
				return Status;
			}
			else
			{
				return Status.Error;
			}
		}

		public void Reset ()
		{
			if (!IsInactive)
			{
				OnReset ();
			}
		}

		public virtual StringBuilder BuildString (StringBuilder builder = null, int depth = 0)
		{
			if (builder == null)
			{
				builder = new StringBuilder ();
			}
			string str = Name + ": " + Status;
			for (int i = 0; i < depth; i++)
			{
				builder.Append ("    ");
			}
			builder.Append (str);
			builder.Append ("\n");
			return builder;
		}

		#region virtual
		/// <summary>
		/// this is called on first update, where 
		/// </summary>
		protected virtual bool OnStart ()
		{
			return true;
		}

		/// <summary>
		/// Set node Status and info here
		/// This is where actually logic happens
		/// </summary>
		protected abstract Status OnUpdate ();


		protected virtual bool OnPreUpdate ()
		{
			return true;
		}

		protected virtual bool OnPostUpdate ()
		{
			if (IsInactive)
			{
				Error ("Node is Inactive after run");
				return false;
			}
			return true;
		}

		protected virtual void OnReset ()
		{
			Status = Status.Inactive;
			Information = null;
		}

		protected void Error (string msg)
		{
			Status = Status.Error;
			Information = msg;
		}

		protected virtual void SetMessage (string msg)
		{
			Information = msg;
		}

		protected void _AddChild (BTNode child)
		{
			child.ParentNode = this;
		}

		protected virtual string GetShowName ()
		{
			string name = GetType ().Name;
			if (name.EndsWith ("Node"))
			{
				return name.Remove (name.Length - 4, 4);
			}
			else
			{
				return name;
			}
		}
		#endregion
	}

	public static class BTNodeUtility
	{
		public static TNode SetName<TNode> (this TNode node, string name)
			where TNode : BTNode
		{
			node.Name = name;
			return node;
		}
	}
}